#pragma once

#include "Framebuffer.h"
#include "Texture.h"

namespace Gut
{
	struct RenderTarget
	{
		FrameBuffer frameBuffer;
		std::vector<Texture2D> outColors;

		Texture2DDesc texDesc;

		Texture2D depthMap;
		Texture2DDesc depthDesc;

		RenderBuffer rBuffer;
		int mWidth, mHeight;

		struct Desc
		{
			int width, height;
			int numTexture;
			int format = GL_RGBA;
			int internalFormat = GL_RGBA;
			int dataType = GL_FLOAT;
			int target = GL_TEXTURE_2D;
		};
		void Create(const Desc &desc)
		{
			mWidth = desc.width;
			mHeight = desc.height;

			frameBuffer.Create();

			texDesc.target = desc.target;
			texDesc.width = desc.width;
			texDesc.height = desc.height;
			texDesc.internalformat = desc.internalFormat;
			texDesc.format = desc.format;
			texDesc.data = 0;
			texDesc.dataType = desc.dataType;

			std::vector<unsigned int> attachments;

			for (int i = 0; i < desc.numTexture; i++)
			{
				Texture2D outColor;
				outColor.Create();
				outColor.TexImage2D(texDesc);
				outColor.Bind();

				SamplerDesc samDesc;
				samDesc.minFilter = GL_LINEAR;
				samDesc.magFilter = GL_LINEAR;

				outColor.SetSampler(samDesc);
				outColor.Mipmap();

				frameBuffer.BufferTexture2D(GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, outColor.ID);
				attachments.emplace_back(GL_COLOR_ATTACHMENT0 + i);

				outColors.emplace_back(outColor);
			}

			depthDesc.target = desc.target;
			depthDesc.width = desc.width;
			depthDesc.height = desc.height;
			depthDesc.internalformat = GL_DEPTH_COMPONENT;
			depthDesc.format = GL_DEPTH_COMPONENT;
			depthDesc.dataType = desc.dataType;
			depthDesc.data = 0;

			depthMap.Create();
			depthMap.TexImage2D(depthDesc);
			SamplerDesc samDesc;
			samDesc.minFilter = GL_NEAREST;
			samDesc.magFilter = GL_NEAREST;
			samDesc.wrapS = GL_CLAMP_TO_EDGE;
			samDesc.wrapT = GL_CLAMP_TO_EDGE;
			samDesc.useBorderColor = true;
			samDesc.borderColor[0] = 1.0f;
			samDesc.borderColor[1] = 1.0f;
			samDesc.borderColor[2] = 1.0f;
			samDesc.borderColor[3] = 1.0f;
			depthMap.SetSampler(samDesc);

			attachments.emplace_back(GL_DEPTH_ATTACHMENT);

			frameBuffer.DrawBuffers(&attachments[0], attachments.size());

			rBuffer.Create();
			rBuffer.Storage(GL_DEPTH24_STENCIL8, desc.width, desc.height);
			frameBuffer.RenderBuffer(GL_DEPTH_STENCIL_ATTACHMENT, rBuffer.ID);
			frameBuffer.CheckError();
		}

		void SetWindowSize(int width, int height)
		{
			if (texDesc.width == width && texDesc.height == height)
			{
				return;
			}


			texDesc.width = width;
			texDesc.height = height;

			for (auto &outColor : outColors)
			{
				outColor.TexImage2D(texDesc);
			}
			depthDesc.width = width;
			depthDesc.height = height;
			depthMap.TexImage2D(depthDesc);

			rBuffer.Storage(GL_DEPTH24_STENCIL8, width, height);

			mWidth = width;
			mHeight = height;
		}
		void Begin()
		{
			frameBuffer.Bind();
			glViewport(0, 0, mWidth, mHeight);
		}

		void End()
		{
			frameBuffer.UnBind();
		}

		struct ScreenShotDesc
		{
			int attachment = GL_COLOR_ATTACHMENT0;

			int x = 0;
			int y = 0;
			int width = 1;
			int height = 1;
			int format = GL_FLOAT;
			int type = GL_RGBA;
			void *out = 0;
		};

		void SceenShot(const ScreenShotDesc &desc)
		{
			frameBuffer.Bind();
			glReadBuffer(desc.attachment);
			glReadPixels(desc.x, desc.y, desc.width, desc.height, desc.format, desc.type, desc.out);
			frameBuffer.UnBind();
		}

		int ReadOneRGBA(float xa, float ya, int attachmentOffset)
		{
			unsigned char udata[4];

			Gut::RenderTarget::ScreenShotDesc desc;

			desc.attachment = GL_COLOR_ATTACHMENT0 + attachmentOffset;
			desc.width = 1;
			desc.height = 1;
			desc.x = xa * mWidth;
			desc.y = mHeight * ya;
			desc.out = udata;
			desc.format = GL_RGBA;
			desc.type = GL_UNSIGNED_BYTE;

			SceenShot(desc);


			unsigned r = udata[0];
			unsigned g = udata[1];
			unsigned b = udata[2];
			unsigned a = udata[3];

			int objectID = ((int)r << 24) | ((int)g << 16) | ((int)b << 8) | (int)a;
			return objectID;

		}
	};

}